package com.jiahe.video.service.impl;

import ai.onnxruntime.OrtException;
import cn.hutool.core.date.DateUtil;
import com.jiahe.video.ai.base.Onnx;
import com.jiahe.video.ai.base.Output;
import com.jiahe.video.config.AppConfig;
import com.jiahe.video.model.Camera;
import com.jiahe.video.service.ApiService;
import com.jiahe.video.service.AppService;
import com.jiahe.video.util.AppUtil;
import com.jiahe.video.util.ImageArea;
import com.jiahe.video.util.ImageUtil;
import lombok.extern.slf4j.Slf4j;
import nu.pattern.OpenCV;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt;
import org.opencv.core.Point;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;

@Slf4j
@Service
public class AppServiceImplV3 extends AbsCommonService implements AppService {


    @Resource
    private AppConfig appConfig;

    // 保存图片的清晰度 (0,100]
    final int CLARITY = 60;
    // 打码
    final float mosaicScoreMin = 0.5F;
    // 人物
    final float personScoreMin = 0.85F;
    // 视频画面的缩放比例 (0,100]

    @Resource
    private Onnx onnx;

    @Resource
    private ApiService apiService;
    @Resource
    private ExecutorService executorService;

    @Override
    public void buildWorker(Camera camera) {

        log.info("任务创建:camera->{},", camera.getId());
        executorService.submit(() -> {
            try {
                this.detection(camera, 2, 60);
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
        });


        /*CompletableFuture.runAsync(() -> {
            try {
                this.detection(camera, 2, 60);
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
        });*/
    }

    @Override
    public void heartbeat(Camera camera) {
        log.info("心跳,carmeId:{}", camera.getId());
    }

    @Override
    public void detectionOnce(Camera camera) {
        log.info("单次检测，cameraId:{}", camera.getId());
        String sourceFilePath = appConfig.getFilePathRoot() + "/" + camera.getId() + "/source";
        String markFilePath = appConfig.getFilePathRoot() + "/" + camera.getId() + "/mark";
        String mosaicFilePath = appConfig.getFilePathRoot() + "/" + camera.getId() + "/mosaic";

        OpenCV.loadLocally();
        // 获取操作系统信息
        String OS = System.getProperty("os.name").toLowerCase();
        // 如果是Windows系统，加载FFmpeg相关的OpenCV动态链接库
        /*if (OS.contains("win")) {
            //System.load(ClassLoader.getSystemResource("lib/opencv_videoio_ffmpeg470_64.dll").getPath());
            System.load(appConfig.getDllPath());
        }*/

        // 创建视频捕获对象
        VideoCapture video = new VideoCapture();
        log.info("尝试连接视频流: {}", camera.getRtsp());
        if ( !video.open(camera.getRtsp()) ) {
            log.error("{},打开Rtsp失败...", camera.getRtsp());
        }

        // 存储读取的视频帧
        Mat img = new Mat();
        video.read(img);

        // 行人检测帧
        Mat checkkMat = new Mat();
        Mat markMat = new Mat();

        try {

            /* 【 业 务 开 始 】 */
            // 文件名
            String fileName = DateUtil.format(new Date(), "YYYYMMddHHmmss") + ".jpeg";

            /* 【 step-A  识别图片，并保存标记图 】 */
            // A.1识别
            checkkMat = img.clone();
            markMat = img.clone();
            List<Output> outputs = onnx.run(checkkMat);
            //log.info("cameraID：{},检测完毕,结果:{}",camera.getId(),outputs.size());
            // A.2 保存标记图片
            AppUtil.createDirectoryIfNotExists(markFilePath);
            ArrayList<Output> personOutputs = new ArrayList<>();
            for (Output output : outputs) {
                if(output.getScore()<= personScoreMin ) {
                    continue;
                }
                personOutputs.add(output);
            }
            onnx.drawprocess(personOutputs, markMat, markFilePath + "/" + fileName);

            /* 【 step-B  保存原图，将原图打码并另存为 】 */
            // B.1 保存原图
            AppUtil.createDirectoryIfNotExists(sourceFilePath);
            Imgcodecs.imwrite(sourceFilePath + "/" + fileName, img,new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, CLARITY));

            // B.2 打码，另存为
            List<ImageArea> imageAreaList = new ArrayList<>();
            for (Output output : outputs) {
                if(output.getScore()<= mosaicScoreMin ) {
                    continue;
                }
                Point topLeft = new Point(output.getLocation().get(0).get("x"), output.getLocation().get(0).get("y"));
                Point bottomRight = new Point(output.getLocation().get(2).get("x"), output.getLocation().get(2).get("y"));
                imageAreaList.add(new ImageArea( (int)Math.round(topLeft.x),(int)Math.round(topLeft.y),(int)Math.round(bottomRight.x - topLeft.x),(int)Math.round(bottomRight.y - topLeft.y)));
            }
            AppUtil.createDirectoryIfNotExists(mosaicFilePath);
            ImageUtil.mosaic(sourceFilePath + "/" + fileName,
                    mosaicFilePath + "/" + fileName,
                    imageAreaList, 10);

            // B.3 登记组内
            if (!"0".equals(camera.getGroupId())) {
                notice(camera,personOutputs.size(),markFilePath + "/" + fileName);
            }

            /* 【 step-C  文件上传 】 */
            if (camera.getIsUpload()==1) {
                CompletableFuture.runAsync(() -> {
                    try {
                        apiService.uploadImage(camera,mosaicFilePath + "/" + fileName);
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.error(e.getMessage());
                    }
                });
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        } finally {
            img.release();
            if (markMat != null) {
                markMat.release();
            }
            if (checkkMat != null) {
                checkkMat.release();
            }
        }


    }

    /**
     * @param camera          摄像头信息
     * @param checkPersonStep 行人检测频率
     * @param uploadImageStep 图片上传频率
     */
    private void detection(Camera camera, long checkPersonStep, long uploadImageStep) throws IOException, OrtException {

        String sourceFilePath = appConfig.getFilePathRoot() + "/" + camera.getId() + "/source";
        String markFilePath = appConfig.getFilePathRoot() + "/" + camera.getId() + "/mark";
        String mosaicFilePath = appConfig.getFilePathRoot() + "/" + camera.getId() + "/mosaic";

        OpenCV.loadLocally();
        // 获取操作系统信息
        String OS = System.getProperty("os.name").toLowerCase();
        // 如果是Windows系统，加载FFmpeg相关的OpenCV动态链接库
        /*if (OS.contains("win")) {
            System.load(ClassLoader.getSystemResource("lib/opencv_videoio_ffmpeg470_64.dll").getPath());
        }*/

        // 行人检测
        boolean checkPersonNeed;
        long checkPersonLastTime = System.currentTimeMillis();

        // 图片上传
        boolean uploadImageNeed;
        long uploadImageLastTime = System.currentTimeMillis();

        long heartbeatLastTime = System.currentTimeMillis();

        // 存储读取的视频帧
        Mat img = new Mat();

        // 重连配置参数
        int maxReconnectAttempts = 10; // 最大重连次数
        long reconnectInterval = 100; // 重连间隔(毫秒)
        int reconnectCount = 0; // 当前重连次数计数

        while (true) {
            // 创建视频捕获对象
            VideoCapture video = new VideoCapture();
            boolean isConnected = false;

            // 设置视频参数
            video.set(Videoio.CAP_PROP_BITRATE, 1000000); // 设置比特率为2Mbps 参考值：2000000
            video.set(Videoio.CAP_PROP_FPS, 10); // 设置帧率 参考值：25

            // 尝试连接视频流
            log.info("尝试连接视频流: {}", camera.getRtsp());
            isConnected = video.open(camera.getRtsp());

            if (!isConnected) {
                log.error("视频流连接失败，准备重试");
                throw new IOException("无法打开RTSP流: " + camera.getRtsp());
            }

            // 连接成功，重置重连计数
            reconnectCount = 0;
            log.info("视频流连接成功");

            while (video.read(img)) {
                if (img.empty()) {
                    log.error("空帧跳过处理: {}", camera.getId());
                    continue;
                }
                // 检查图像尺寸是否合法（避免0尺寸导致resize失败）
                if (img.rows() == 0 || img.cols() == 0) {
                    log.error("无效图像尺寸: {}x{}，跳过resize", img.rows(), img.cols());
                    continue;
                }


                // 行人检测帧
                Mat checkkMat = new Mat();
                Mat markMat = new Mat();

                try {
                    // 心跳
                    if ((System.currentTimeMillis() - heartbeatLastTime) / 1000 > 10) {
                        this.heartbeat(camera);
                        heartbeatLastTime = System.currentTimeMillis();
                    }
                    // 频率校验
                    checkPersonNeed = false;
                    uploadImageNeed = false;

                    if ((System.currentTimeMillis() - checkPersonLastTime) / 1000 > checkPersonStep) {
                        checkPersonNeed = true;
                    }
                    if ((System.currentTimeMillis() - uploadImageLastTime) / 1000 > uploadImageStep) {
                        uploadImageNeed = true;
                    }

                    // 若： 不需要行人检测 也 不需要上传图片
                    if (!checkPersonNeed && !uploadImageNeed) {
                        continue;
                    }

                    if (checkPersonNeed) {
                        checkPersonLastTime = System.currentTimeMillis();
                    }
                    if (uploadImageNeed) {
                        uploadImageLastTime = System.currentTimeMillis();
                    }

                    /* 【 业 务 开 始 】 */
                    // 文件名
                    String fileName = DateUtil.format(new Date(), "YYYYMMddHHmmss") + ".jpeg";

                    /* 【 step-A  识别图片，并保存标记图 】 */
                    // A.1识别
                    checkkMat = img.clone();
                    markMat = img.clone();
                    List<Output> outputs = onnx.run(checkkMat);
                    //log.info("cameraID：{},检测完毕,结果:{}",camera.getId(),outputs.size());
                    // A.2 保存标记图片
                    AppUtil.createDirectoryIfNotExists(markFilePath);
                    ArrayList<Output> personOutputs = new ArrayList<>();
                    for (Output output : outputs) {
                        if(output.getScore()<= personScoreMin ) {
                            continue;
                        }
                        personOutputs.add(output);
                    }
                    onnx.drawprocess(personOutputs, markMat, markFilePath + "/" + fileName);

                    /* 【 step-B  保存原图，将原图打码并另存为 】 */
                    // B.1 保存原图
                    AppUtil.createDirectoryIfNotExists(sourceFilePath);
                    Imgcodecs.imwrite(sourceFilePath + "/" + fileName, img,new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, CLARITY));

                    // B.2 打码，另存为
                    List<ImageArea> imageAreaList = new ArrayList<>();
                    for (Output output : outputs) {
                        if(output.getScore()<= mosaicScoreMin ) {
                            continue;
                        }
                        Point topLeft = new Point(output.getLocation().get(0).get("x"), output.getLocation().get(0).get("y"));
                        Point bottomRight = new Point(output.getLocation().get(2).get("x"), output.getLocation().get(2).get("y"));
                        imageAreaList.add(new ImageArea( (int)Math.round(topLeft.x),(int)Math.round(topLeft.y),(int)Math.round(bottomRight.x - topLeft.x),(int)Math.round(bottomRight.y - topLeft.y)));
                    }
                    AppUtil.createDirectoryIfNotExists(mosaicFilePath);
                    ImageUtil.mosaic(sourceFilePath + "/" + fileName,
                            mosaicFilePath + "/" + fileName,
                            imageAreaList, 10);

                    // B.3 登记组内
                    if (!"0".equals(camera.getGroupId())) {
                        notice(camera,personOutputs.size(),markFilePath + "/" + fileName);
                    }

                    /* 【 step-C  文件上传 】 */
                    if (uploadImageNeed) {
                        CompletableFuture.runAsync(() -> {
                            try {
                                apiService.uploadImage(camera,mosaicFilePath + "/" + fileName);
                            } catch (Exception e) {
                                e.printStackTrace();
                                log.error(e.getMessage());
                            }
                        });
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    log.error(e.getMessage());
                } finally {
                    img.release();
                    if (markMat != null) {
                        markMat.release();
                    }
                }
            }
            // 确保释放视频捕获资源
            if (video != null && video.isOpened()) {
                video.release();
            }
            log.error("cameraId:{},连接断开。",camera.getId());
            // 检查是否需要重连
            reconnectCount++;
            if (reconnectCount >= maxReconnectAttempts) {
                log.error("cameraId:{},已达到最大重连次数({})，停止重试", camera.getId(), maxReconnectAttempts);
                break;
            }
            // 重连前等待一段时间
            try {
                log.info("cameraId:{},等待 {} 毫秒后进行第 {} 次重连", camera.getId(), reconnectInterval, reconnectCount);
                Thread.sleep(reconnectInterval);
            } catch (InterruptedException e) {
                log.warn("重连等待被中断", e);
                Thread.currentThread().interrupt(); // 恢复中断状态
                break;
            }
        }
    }

}
